
#include <apr-1/apr_date.h>
#include <apr-1/apr_strings.h>
#include "../utils/StringUtils.h"
#include "../net/Broker4.h"
#include "DBArbitrator.h"

int DBArbitrator::CheckForUser(AllDBs *dbs, const char *name)
{
   int retval = 0;
   std::vector<char *> *strlist;
   strlist = dbs->pendingdb->sqlScanRowTextf("select count(name) from 'user_requests' where name='%s';", name);
   if ((strlist) && (strlist->size() > 0))
   {
      retval = atoi(strlist->at(0));
      StringUtils::destroyStringList(strlist);
   } else
   {
      if (strlist) delete strlist;
   }
   if (retval)
      return CHECKSTATE_EXISTS_UNAUTHORISED;
   strlist = dbs->keydb->sqlScanRowTextf("select count(keyname) from 'public_key' where keyname='%s';", name);
   if ((strlist) && (strlist->size() > 0))
   {
      retval = atoi(strlist->at(0));
      StringUtils::destroyStringList(strlist);
   } else
   {
      if (strlist) delete strlist;
   }
   if (retval)
      return CHECKSTATE_RESERVED;
   return CHECKSTATE_DOES_NOT_EXIST;
}

int DBArbitrator::UserAddRequestAccept(AllDBs *dbs, const char *name, const char *date, const char *publickey)
{
   dbs->keydb->sqlInstantExecf("delete from 'public_key' where keyname='.%s';", name);
   dbs->keydb->AddPublicKey(name, "RSA", publickey, NULL);
   dbs->actorsdb->AddUserToActors(name, date, 0, (apr_size_t)64*1024*1024*1024);
   dbs->clustersdb->CreateUserTable(name);
   return 0;
}

int DBArbitrator::DeleteFileStoreByUUID(AllDBs *dbs, const char *uuid, apr_pool_t *mp)
{
   // Now do the file clean up
   std::vector<char *> *info = dbs->actorsdb->ViewWorkStoreByUUID(uuid);
   if ((info == NULL) || (info->size() == 0))
   {
      errprint("tried to delete '%s' which wasn't there!", uuid);
      if (info) delete info;
      return -1;
   }
   int noofblocks = atoi(info->at(1));
   for (int i = 0; i < noofblocks; i++)
   {
      char *filename;
      filename = StringUtils::varprintf("_%s.c%d", uuid, i);
      apr_size_t fsize = Broker4::GetFileSize(filename, mp);
      apr_file_remove(filename, mp);
      char *ultuser = dbs->actorsdb->LookupUltimateUser(info->at(3));
      if (ultuser) dbs->actorsdb->ModifyUserQuota(ultuser, fsize);
      free(ultuser);
      free(filename);
   }
   for (int i = 0; i < noofblocks; i++)
   {
      char *filename;
      filename = StringUtils::varprintf("_%s.f%d", uuid, i);
      apr_size_t fsize = Broker4::GetFileSize(filename, mp);
      apr_file_remove(filename, mp);
      char *ultuser = dbs->actorsdb->LookupUltimateUser(info->at(3));
      if (ultuser) dbs->actorsdb->ModifyUserQuota(ultuser, fsize);
      free(ultuser);
      free(filename);
   }
   UnuseAndDeleteAllKeysDependentOnUUID(dbs, uuid);
   dbs->actorsdb->sqlInstantExecf("delete from actors where name = '%s';", uuid);
   dbs->actorsdb->sqlInstantExecf("delete from workdata where file_store = '%s';", uuid);
   dbs->actorsdb->sqlInstantExecf("delete from work_stores where name = '%s';", uuid);
   dbs->keydb->DeletePublicKeyByName(uuid);
   dbs->keydb->DeletePrivateKeyByName(uuid);
   char *jinname = StringUtils::varprintf("in_%s", uuid);
   dbs->keydb->DeletePublicKeyByName(jinname);
   free(jinname);
   char *joutname = StringUtils::varprintf("out_%s", uuid);
   dbs->keydb->DeletePrivateKeyByName(joutname);
   dbs->keydb->DeletePublicKeyByName(joutname);
   free(joutname);
   char *fname = StringUtils::varprintf("_%s.xml", uuid);
   apr_file_remove(fname, mp);
   StringUtils::destroyStringList(info);
   dbs->keydb->sqlInstantExecf("delete from private_key where owner = '%s';", uuid);
   dbs->keydb->sqlInstantExecf("delete from public_key where owner = '%s';", uuid);
   dbs->keydb->sqlInstantExecf("delete from private_key where owner = '.compute_%s';", uuid);
   dbs->keydb->sqlInstantExecf("delete from public_key where owner = '.compute_%s';", uuid);
   return 0;
}

void DBArbitrator::CleanExpiredComputeSessions(AllDBs *dbs)
{
   std::vector<char *> *fs_info = dbs->actorsdb->sqlScanAllTextf("select name,begindate,duration from compute_sessions;");
   if ((fs_info == NULL) || (fs_info->size() == 0))
   {
      if (fs_info) delete fs_info;
      return;
   }
   int rowno = fs_info->size() / 3;
   for (int i = 0; i < rowno; i++)
   {
      apr_time_t timet = apr_time_now();
      apr_time_t timebefore = apr_date_parse_rfc(fs_info->at(i*3 + 1));
      // microseconds to seconds
      if (apr_time_sec(timet - timebefore) > atoi(fs_info->at(i*3 + 2)))
      {
         dbs->actorsdb->sqlInstantExecf("delete from 'actors' where name = '%s';", fs_info->at(i*3 + 0));
         UnuseAndDeleteAllKeysDependentOnUUID(dbs, fs_info->at(i*3 + 0));
         dbs->actorsdb->sqlInstantExecf("delete from 'compute_sessions' where name = '%s';", fs_info->at(i*3 + 0));
         dbs->keydb->sqlInstantExecf("delete from 'public_key' where keyname = '%s';", fs_info->at(i*3 + 0));
         dbs->keydb->sqlInstantExecf("delete from 'private_key' where keyname = 'b_%s';", fs_info->at(i*3 + 0));
         dbs->keydb->sqlInstantExecf("delete from 'public_key' where keyname = 'b_%s';", fs_info->at(i*3 + 0));
      }
   }
   StringUtils::destroyStringList(fs_info);
}

char *DBArbitrator::LookupComputeUltimateUser(AllDBs *dbs, const char *uuid)
{
   if ((uuid == NULL) || (strlen(uuid) == 0))
   {
      errprint("cannot obtain the username of %s compute UUID", (uuid) ? "an empty" : "a NULL");
      return NULL;
   }
   char *workstore = dbs->keydb->GetWorkStoreFromComputeKey(uuid);
   if (workstore)
   {
      char *ultuser = dbs->actorsdb->LookupUltimateUser(workstore);
      free(workstore);
      return ultuser;
   } else
   {
      errprint("could not find workstore for compute UUID '%s'", uuid);
      return NULL;
   }
}

void DBArbitrator::UnuseAndDeleteAllKeysDependentOnUUID(AllDBs *dbs, const char *uuid)
{
   std::vector<char *> *allkeys = dbs->actorsdb->sqlScanAllTextf("select keyname from key_dependencies where uuidname = '%s';", uuid);
   if ((allkeys == NULL) || (allkeys->size() == 0))
   {
      if (allkeys) delete allkeys;
   } else
   {
      for (int i = 0; i < allkeys->size(); i++)
      {
         dbs->keydb->KeyNowUnusedByActor(allkeys->at(i));
         dbs->keydb->KeyNowDeletedByActor(allkeys->at(i));
      }
      StringUtils::destroyStringList(allkeys);
      dbs->actorsdb->sqlInstantExecf("delete from key_dependencies where uuidname = '%s';", uuid);
   }
}

void DBArbitrator::ScheduleComputeResources(int instances, const char *clusterid, const char *password, apr_pool_t *imp)
{
   apr_status_t status;
   apr_pool_t *mp;
   status = apr_pool_create(&mp, imp);
   char *clusters_list = (char *)apr_pstrdup(mp, clusterid);
   char *remainder = NULL;
   char *ttoken = apr_strtok(clusters_list, ",", &remainder);
   std::vector<char *> *tcluster_list = new std::vector<char *>();
   while (ttoken != NULL)
   {
      tcluster_list->push_back(ttoken);
      ttoken = apr_strtok(NULL, ",", &remainder);
   }
   // we have a complete cluster list now, but we need to do something with it!
   
   apr_pool_destroy(mp);
}
